# This file is common place for activity design. Since
# Activity design does not use Django specific design
# this file will contain base design functionality
import neo4j
from datetime import datetime
from time import strftime
from connectpeople import Constants, settings
import hashlib
from django.http import QueryDict
from django.core import serializers

# Create new Activity nodes
def setActivityNode(descr, userid, grp_web_addr, action_type):
    
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )

    # Get Current Time
    time = datetime.now().strftime('%Y, %b, %d, %T')
    
    # Generate Unique index for activity using Hash
    id = hashlib.md5(userid + grp_web_addr + descr + time).hexdigest()

    with graphDb.transaction:
        # Create Activity node        
        activity = graphDb.node(activity_id=id,
                                timestamp=time,
                                description=descr)
        
        # Get Action Node
        action = getActiionNode(graphDb, action_type)
        # Define Activity to Action Relationship
        activity.OF_TYPE(action)
        
        # Get Rating Node - Initially Assign Activity as good (3)
        rating = getRatingNode(graphDb)
        # Define Activity to Rating Relationship
        activity.HAS_RATING(rating)
        
        # Get User Node
        user = getUserNode(graphDb, userid)
        # Define Activity to User Relationship
        activity.ACTIVITY_CREATED_BY(user)
        
        # Get Group Node
        group = getGroupNode(graphDb, grp_web_addr)
        # Define Activity to Group Relationship
        activity.ACTIVITY_FOR_GROUP(group)        
        
        # Create Activity index
        activ_index = graphDb.index(Constants.activity_index, create=True)
        # Assign newly created activity node to the index
        activ_index[id] = activity
    graphDb.shutdown()

# Get Activity Node
def getActivityNode(graphDb, index):
    # Get Activity Index
    act_index = graphDb.index(Constants.activity_index, create=True)
    # Get Activity Node from index
    activity = act_index[index] 
    
    return activity

# Create Comment Node
def setCommentNode(acti_index, descr, userid):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )
    
    # Get Current Time
    time = datetime.now().strftime('%Y, %b, %d, %T')
    
    # Generate Unique index for Comment using Hash
    id = hashlib.md5(acti_index + userid + time + descr).hexdigest()
    
    with graphDb.transaction:
        # Create Comment node        
        comment = graphDb.node(comment_id=id,
                                timestamp=time,
                                description=descr)
        
        # Get Activity Node from index
        activity = getActivityNode(graphDb, acti_index)
        # Define Comment to Activity Relationship
        comment.COMMENT_WRITTEN_ON_ACTIVITY(activity)
        
        # Get User Node
        user = getUserNode(graphDb, userid)        
        # Define Activity to User Relationship
        comment.COMMENT_CREATED_BY(user)        
        
        # Create Comment index
        cmnt_index = graphDb.index(Constants.comment_index, create=True)
        # Assign newly created Comment node to the index
        cmnt_index[id] = comment
    graphDb.shutdown()    

# Get Comment Node
def getCommentNode(graphDb, index):
    # Get Comment Index
    comment_index = graphDb.index(Constants.comment_index, create=True)
    # Get Comment Node from index
    comment = comment_index[index]   
    
    return comment

# Create Other Activity Node
def setOtherActivityNode(index, typ, val, userid):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )
    
    # Get Current Time
    time = datetime.now().strftime('%Y, %b, %d, %T')
    
    # Generate Unique index for Other Activity using Hash
    id = hashlib.md5(index + userid + time).hexdigest()
    
    with graphDb.transaction:
        # Create Other Activity node        
        other_activity = graphDb.node(other_activity_id=id,
                                type=typ,
                                value = val,
                                timestamp=time)
        
        if index.startswith("comm_other_act_"):
            # Get Comment Node from index
            comment = getCommentNode(graphDb, index.replace("comm_other_act_", ""))
            # Define Other Activity to Comment Relationship
            other_activity.OTHER_ACTIVITY_TO_COMMENT(comment)
        elif index.startswith("other_act_"):
            # Get Activity Node from index
            activity = getActivityNode(graphDb, index.replace("other_act_", ""))
            # Define other activity to Activity Relationship
            other_activity.OTHER_ACTIVITY_TO_ACTIVITY(activity)    
        
        # Get User Node
        user = getUserNode(graphDb, userid)
        # Define Activity to User Relationship
        other_activity.OTHER_ACTIVITY_CREATED_BY(user)          
        
        # Create Sub Comment index
        othr_act_index = graphDb.index(Constants.other_activity_index, create=True)
        # Assign newly created Sub Comment node to the index
        othr_act_index[id] = other_activity
    graphDb.shutdown()

# Get Other Activity Node
def getOtherActivityNode(index):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )
    
    with graphDb.transaction:
        # get the Other Activity Index
        othr_act_index = graphDb.index(Constants.other_activity_index, create=True)
        # Get the Comment node based on index
        other_activity = othr_act_index[index]       
    graphDb.shutdown()
    
    return other_activity


# Create User Node
def setUserNode(userid):
    
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )

    with graphDb.transaction:
        # Create User node        
        user = graphDb.node(username=userid)        
        
        # Create User index
        usr_index = graphDb.index(Constants.user_index, create=True)
        # Assign newly created User node to the index
        usr_index[userid] = user
    graphDb.shutdown()

# Get User Node
def getUserNode(graphDb, index):    
    # Get User Index
    usr_index = graphDb.index(Constants.user_index, create=True)
    # Get User Node
    user = usr_index[index]
    
    return user

# Create User Node
def setGroupNode(group_web_addr, userid, grp_name):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )

    with graphDb.transaction:
        # Create Group node        
        group = graphDb.node(group_web_address=group_web_addr,
                             name=grp_name)        
        
        # Get User Index
        usr_index = graphDb.index(Constants.user_index, create=True)
        # Get User Node
        user = usr_index[userid]        
        # Define Activity to User Relationship
        user.CONNECTED_TO(group)         
        
        # Create Group index
        grp_index = graphDb.index(Constants.group_index, create=True)
        # Assign newly created Group node to the index
        grp_index[group_web_addr] = group
    graphDb.shutdown()

# Get Group Node
def getGroupNode(graphDb, index):
    # get the Group Index
    grp_index = graphDb.index(Constants.group_index, create=True)
    # Get the group node based on index
    if index:
        group = grp_index[index]
    else:
        group = None
    return group

# Connect newly joined user to the Group
def connectUserToGroup(group_web_addr, userid):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )

    with graphDb.transaction:
        # Get group node
        group = getGroupNode(graphDb, group_web_addr)
        
        # Get user node
        user = getUserNode(graphDb, userid)
        
        # Connect to the group
        user.CONNECTED_TO(group)
    graphDb.shutdown()
    
# Get Group Node
def getActiionNode(graphDb, index):
    # Get Action Index
    act_index = graphDb.index(Constants.action_index, create=True)        
    # Get Action Node
    action = act_index[index]
    
    return action

# Get Group Node
def getRatingNode(graphDb):
    # Get Rating Index
    rat_index = graphDb.index(Constants.rating_index, create=True)
    # Get Rating Node - Initially Assign Activity as good (3)
    rating = rat_index[Constants.good_rating] 
    
    return rating

#####################################################
########## Lookup classes ###########################
#####################################################

# Breadth First Search for getting user for givev activity
class activity_user(neo4j.Traversal):  
    types = [ neo4j.Outgoing.ACTIVITY_CREATED_BY]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE
    
# Breadth First Search for getting all the activities related to a usernode
class user_activities(neo4j.Traversal):  
    types = [ neo4j.Incoming.ACTIVITY_CREATED_BY]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.STOP_AT_END_OF_GRAPH
    returnable = neo4j.RETURN_ALL_BUT_START_NODE

# Breadth First Search for getting all the activities related to a groupnode
class group_activities(neo4j.Traversal):  
    types = [ neo4j.Incoming.ACTIVITY_FOR_GROUP ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE
    
# Breadth First Search for getting the action of the activitynode
class activity_action(neo4j.Traversal):  
    types = [ neo4j.Outgoing.OF_TYPE ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE
    
# Breadth First Search for getting the comments of the activitynode
class activity_comments(neo4j.Traversal):  
    types = [ neo4j.Incoming.COMMENT_WRITTEN_ON_ACTIVITY ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE

# Breadth First Search for getting the user of the comment
class comment_user(neo4j.Traversal):  
    types = [ neo4j.Outgoing.COMMENT_CREATED_BY ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE


# Breadth First Search for getting the other activity of the activitynode
class activity_other_act(neo4j.Traversal):  
    types = [ neo4j.Incoming.OTHER_ACTIVITY_TO_ACTIVITY ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE

# Breadth First Search for getting the other activity of the commentnode
class comment_other_act(neo4j.Traversal):  
    types = [ neo4j.Incoming.OTHER_ACTIVITY_TO_COMMENT ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE

# Breadth First Search for getting the user of the other activity node
class other_act_user(neo4j.Traversal):  
    types = [ neo4j.Outgoing.OTHER_ACTIVITY_CREATED_BY ]
    order = neo4j.BREADTH_FIRST
    stop = neo4j.StopAtDepth(1)
    returnable = neo4j.RETURN_ALL_BUT_START_NODE

#####################################################
#### Process Lookup classes to find nodes ###########
#####################################################

# Get Group activities
def getGroupActivities(mem_grp_data):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )
    
    activity_list = []
    with graphDb.transaction:
        for each_row in mem_grp_data:
            grp_web_addr = each_row.group_web_address
            
            # Get the group node based on index
            group = getGroupNode(graphDb, grp_web_addr)
            
            if group:
                for each in group_activities(group):
                    activity_node = []
                    activity_node.append(each['activity_id'])
                    activity_node.append(each['timestamp'])
                    activity_node.append(each['description'])
                    
                    # Get the Activity Action type
                    for action in activity_action(each):
                        activity_node.append(action['name'])
                        
                    # Get the user who posted activity
                    for user in activity_user(each):
                        activity_node.append(user['username'])
                        
                    # Get comments on the activity
                    activity_node.append(getCommentsForActivity(each))
                    
                    # Get Other activity to activity                        
                    activity_node.append(getOtherActivitiesForActivity(each))
                    
                    # Add all the above information to list
                    activity_list.append(activity_node)
    graphDb.shutdown()
    
    return activity_list

# Get User activities
def getUserActivities(userid):
    # Instantiate Graph Database object
    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )

    activity_list = []
    with graphDb.transaction:
        
        user = getUserNode(graphDb, userid)
        
        for each in user_activities(user):
            activity_node = []
            activity_node.append(each['activity_id'])
            activity_node.append(each['timestamp'])
            activity_node.append(each['description'])
            
            # Get the Activity Action type
            for action in activity_action(each):
                activity_node.append(action['name'])
                
            # The user who posted activity
            activity_node.append(userid)
                
            # Get comments on the activity
            activity_node.append(getCommentsForActivity(each))
            
            # Get Other activity to activity                        
            activity_node.append(getOtherActivitiesForActivity(each))
            
            # Add all the above information to list
            activity_list.append(activity_node)
    graphDb.shutdown()
    
    return activity_list

# process Activity node to get Other Activities
def getOtherActivitiesForActivity(activity):
    othr_act_list = []
    # Search for other_activities related to the activity
    for othr_act in activity_other_act(activity):
        othr_act_node = []
        # get node property values
        othr_act_node.append(othr_act['other_activity_id'])
        othr_act_node.append(othr_act['type'])
        othr_act_node.append(othr_act['value'])
        othr_act_node.append(othr_act['timestamp'])
        # Get user who performed the activity
        for user in other_act_user(othr_act):
            othr_act_node.append(user['username'])
        othr_act_list.append(othr_act_node)
    
    return othr_act_list

# process Activity node to get Comments
def getCommentsForActivity(activity):
    comment_list = []
    # Search for comments related to the activity
    for comment in activity_comments(activity):
        comment_node = []
        comment_node.append(comment['comment_id'])
        comment_node.append(comment['timestamp'])
        comment_node.append(comment['description'])
        # Get user who commentd
        for user in comment_user(comment):
            comment_node.append(user['username'])        
        # Get Other activity to comment                         
        comment_node.append(getOtherActivitiesForComment(comment))                        
        comment_list.append(comment_node)
        
    return comment_list

# process Comment node to get Other Activities
def getOtherActivitiesForComment(comment):
    comm_othr_act_list = []
    # Search for other_activity related to the comments
    for comment_oth_act in comment_other_act(comment):
        comm_othr_act_node = []
        comm_othr_act_node.append(comment_oth_act['other_activity_id'])
        comm_othr_act_node.append(comment_oth_act['type'])
        comm_othr_act_node.append(comment_oth_act['value'])
        comm_othr_act_node.append(comment_oth_act['timestamp'])
        # Get user who performed other_act
        for user in other_act_user(comment_oth_act):
            comm_othr_act_node.append(user['username'])
        comm_othr_act_list.append(comm_othr_act_node)  

    return comm_othr_act_list

# Get User activities
#def getUserActivities(userid):
#    # Instantiate Graph Database object
#    graphDb = neo4j.GraphDatabase( settings.NEO4J_RESOURCE_URI )
#    
#    import pdb
#    pdb.set_trace()
#    
#    with graphDb.transaction:
#        # get the Other Activity Index
#        usr_index = graphDb.index(Constants.user_index, create=True)
#        # Get the Comment node based on index
#        user = usr_index[userid]
#        username= user['username']
#        activity_list = []    
#        for each in activity_user(user):
#            activity_list.append(each)            
#    graphDb.shutdown()
#    
#    return activity_list